package omq.filetransfer;

import java.io.EOFException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.rmi.RemoteException;

import javax.xml.rpc.ServiceException;

import omq.api.OMQConnection;
import omq.api.OMQConnectionFactory;
import omq.api.OMQConnectionSOAP;
import omq.api.OMQMessage;
import omq.api.OMQQueue;
import omq.api.OMQQueueManager;
import omq.api.OMQSession;
import omq.api.logger.OMQLogger;
import omq.exception.OMQPropertiesReaderException;
import omq.file.OMQFile;
import omq.utils.FileUtil;
import omq.utils.OMQConversions;
import omq.utils.OMQPropertiesReader;
import omq.xml.OMQXmlUtil;

/*
 * OMQ FileTransfer:
 * -----------------
 * 
 * OMQ FileTransfer system is a fully operational file system transfer protocol over OMQ.
 * It sends files to messages in OMQ queue and later receives them back to files.
 *  
 * All system parameters (including OMQ parameters and file-system parameters) are configured using these 2 properties file: 
 * 		for file sender   - omqsend.properties
 * 		for file receiver - omqreceive.properties
 * 
 * Following are the runnable options:
 *    
 * OMQSendFile - sends one specific file specified in parameter omq.ft.filename to a queue.
 * OMQSendFiles - a deamon that wait for new files on the folder omq.ft.path and sends all the files from this folder.
 * OMQReceiveFile - receives only one file from queue into the folder omq.ft.path. 
 * OMQReceiveFiles - a deamon that samples the queue every interval and saves the messages as files. 
 * there are 2 properties file to configure.
 *  
 *  Author  	  	: Hagai Izenberg
 *  Creating Date 	: 20-05-2008
 *  Company 		: OMQI
 */

public class OMQFileTransfer {

	private final static String ftVersion = "2";
	// Path for sending/receiving files
	private String ftPath = null;
	
	private String omqUser = null;
	private String omqPassword = null;
	private String omqQueueManager = null;
	private String omqQueue = null;
	private String omqSOAPServer = null;
	private boolean useTimeStamp ;
	private boolean getUnknown ;
	private String lastMsgID;
	String msgType = null;
	String msgEncoding = null;
	int lastRC = 0;
	
	// max file size in kilobytes
	final int maxFileSize = 50;
	
	private OMQConnection connection = null;
	private OMQSession session = null;
	private OMQMessage message = null;
//	private Map omqParams = new HashMap();
	public OMQPropertiesReader propertiesReader;
	private int[] unaccaptableRCs = {201 // Unknown queue
			,202 //Unknown queue for specified queue manager
			,203 //Unknown queue manager
			,101 //Authentication failed
			,204 //User is not authorized as viewer for queue 
			,205 //User is not authorized as moderator for queue
			,211 //User is not authorized as viewer for queue manager
			,212 //User is not authorized as moderator for queue manager
			,216 //Queue is disabled for sending
			,217 //Queue is disabled for receiving
			,218 //Queue manager is disabled
			,228 //User is not authorized as sender for queue
			,229 //User is not authorized as receiver for queue
			,306 //Message could not be sent because queue has exceeded maximum number of messages
			,308 //Message could not be sent, because one or more destination queues have exceeded maximum number of messages
			};
	
	public OMQFileTransfer(OMQPropertiesReader propertiesReader) throws OMQFileTransferInitException, ServiceException 
	{
		super();
		this.propertiesReader = propertiesReader;		
		initEnv();
	}
	
	private boolean initEnv() throws OMQFileTransferInitException, ServiceException
	{

		OMQLogger.getInstance().info("about to read properties from "+propertiesReader.getPropertiesFileName());
		
		try
		{
			ftPath = FileUtil.addSlashInFilename(propertiesReader.getProperty("omq.ft.path"));
			omqUser = propertiesReader.getProperty("omq.ml.user");
			omqPassword = propertiesReader.getProperty("omq.ml.pwd");
			omqQueueManager = propertiesReader.getProperty("omq.ml.queueMgr");
			omqQueue = propertiesReader.getProperty("omq.ml.queue");
			omqSOAPServer = propertiesReader.getProperty("omq.ml.ws.addr");
			
			OMQLogger.getInstance().info("-----------------------------------------------");
			OMQLogger.getInstance().info("using queue manager named : "+omqQueueManager);
			OMQLogger.getInstance().info("using queue named         : "+omqQueue);
			OMQLogger.getInstance().info("using SOAP server named   : "+omqSOAPServer);
			OMQLogger.getInstance().info("using local path          : "+ftPath);
			OMQLogger.getInstance().info("-----------------------------------------------");

		}
		catch (OMQPropertiesReaderException e)
		{
			OMQLogger.getInstance().fatal("missing property from property file "+propertiesReader.getPropertiesFileName()+", see nested exception for more info",e);
			throw new OMQFileTransferInitException("missing property from property file "+propertiesReader.getPropertiesFileName()+", see nested exception for more info");
		}
		if (!FileUtil.checkIfFileExist(ftPath))
		{ 
			throw new OMQFileTransferInitException("path "+ftPath+" mentioned in properties file as 'omq.ft.path' doesnt exist");
		}
		if (!FileUtil.checkIfFileIsDirectory(ftPath))
		{ 
			throw new OMQFileTransferInitException("path "+ftPath+" mentioned in properties file as 'omq.ft.path' is not a directory");
		}		
		
		connection = OMQConnectionFactory.getConnection(omqUser,omqPassword,omqSOAPServer,new OMQQueueManager(omqQueueManager));
		session = connection.createSession(false);
		
		return true;
		
	}
	public boolean sendFile(String fileName) throws OMQFileTransferException, OMQFileTransferInitException
	{
		OMQLogger.getInstance().info("before sending file "+ftPath+fileName);
		
		OMQLogger.getInstance().debug("checking if file "+ftPath+fileName+" exists");

		if (!FileUtil.checkIfFileExist(ftPath+fileName))
		{
			return false;
		}

		if (!FileUtil.checkIfFileSizeIsMoreThan(ftPath+fileName,maxFileSize))
		{
			return false;
		}
			
		OMQFile fileMessage = new OMQFile();
		try 
		{
			fileMessage.setOmqFileContent(OMQXmlUtil.getBytesFromFile(new File(ftPath+fileName)));

			fileMessage.setOmqFileName(fileName);
			
			String tmpBase64 = OMQConversions.Object2Base64(fileMessage);
					
			OMQLogger.getInstance().info("about to send file '"+ftPath+fileName+"'");
	
			lastRC = session.sendMessage(new OMQQueue(omqQueue), new OMQMessage(fileMessage));
		
			if (lastActionSuccesfull())
			{
				OMQLogger.getInstance().info("sending file '"+ftPath+fileName+"' finished succesfully");
				setLastMsgID(session.getLastMessageID());
				return true;
			}
			else
			{
				OMQLogger.getInstance().info("sending file '"+ftPath+fileName+"' FAILED, rc = "+lastRC+", error description = "+session.getLastErrorDescription());
				if (new Integer(lastRC).intValue() != 301)
				{
					if (new Integer(lastRC).intValue() != 406)
					{
						throw new OMQFileTransferInitException(session.getLastErrorDescription());
					}
				}
				return false;
			}
		} 
		
		catch (java.net.UnknownHostException e)
		{
			// TODO Auto-generated catch block
			OMQLogger.getInstance().error("error communicating with OMQ server",e);
			throw new OMQFileTransferException("error communicating with OMQ server");
		}		
		catch (RemoteException e)
		{
			// TODO Auto-generated catch block
			OMQLogger.getInstance().error("error communicating with OMQ server",e);
			throw new OMQFileTransferException("error communicating with OMQ server");
		}		

		catch (IOException e) 
		{
			// TODO Auto-generated catch block
			OMQLogger.getInstance().error("failed to read file",e);
			throw new OMQFileTransferException("failed to read file");
		}
//		catch (ClassNotFoundException e) 
//		{
//			// TODO Auto-generated catch block
//			OmqLogger.getInstance().error("failed to convert message to file",e);
//			throw new OMQFileTransferException("failed to convert message to file");
//		}		
//		catch (OMQPropertiesReaderException e) 
//		{
//			// TODO Auto-generated catch block
//			OmqLogger.getInstance().error("failed to read property from properties file",e);
//			throw new OMQFileTransferInitException("failed to read property from properties file");
//		}		
		
	}
	
	public boolean receiveFile() throws OMQFileTransferException, OMQFileTransferInitException
	{
		
		OMQLogger.getInstance().info("before getting message from queue");
		
		try
		{
			
			String useTimeStampStr = propertiesReader.getProperty("omq.ft.addtimestamp");
			useTimeStamp = Boolean.parseBoolean(useTimeStampStr);
			if (!useTimeStamp)
			{
//				OmqLogger.getInstance().fatal("omq.ft.addtimestamp not set to true, FILE CONFLICTS COULD OCCUR");
			}
			
			// not implemented yet, messages from unknown sources will be saved to file....
//			String getUnknownStr = propertiesReader.getProperty("omq.ft.unknown.save");
//			getUnknown = Boolean.parseBoolean(getUnknownStr);
//			if (!getUnknown)
//			{
//				OmqLogger.getInstance().fatal("omq.ft.unknown.save not set to true, onrecognized messages will not be saved to file");
//			}
			String unknownFilename = propertiesReader.getProperty("omq.ft.unknown.filename");
		
			message = session.receiveMessage(new OMQQueue(omqQueue)); 
			
			lastRC = session.getLastErrorCode();
			
			if (!lastActionSuccesfull())
			{
				OMQLogger.getInstance().info("receiving message FAILED, rc = "+lastRC+", error description = "+session.getLastErrorDescription());
				if ((new Integer(lastRC).intValue() != 313) && (new Integer(lastRC).intValue() != 310))
				{
					throw new OMQFileTransferInitException(session.getLastErrorDescription());
				}
				return false;
			}
			else
			{
				OMQLogger.getInstance().info("receiving message finished succesfully");
				
				OMQLogger.getInstance().info("trying to convert message to OMQFile");
			
				OMQFile fileMessage;
					
				String currFilename;
				
				String msgid = "";
				String filenamePrefix = "";

				msgid = session.getLastMessageID();
				
				OMQLogger.getInstance().debug("message id of received message is "+msgid);

				filenamePrefix = "QMGR-"+omqQueueManager+"_Q-"+omqQueue+"_MsgID-"+msgid;
				
				if (useTimeStamp)
				{
					filenamePrefix += "_Time-"+FileUtil.time("yyyy-MM-dd_HHmmss");
//					OmqLogger.getInstance().debug("omq.ft.addtimestamp - set to true - adding current timestamp to received files");
				}
				else
				{
//					OmqLogger.getInstance().debug("omq.ft.addtimestamp - not set to true - not adding current timestamp to received files");
				}

				try
				{
//					fileMessage = (OMQFile) OMQConversions.Base642Object(message.getMessage_content());
					fileMessage = (OMQFile) message.getMsgBodyAsObject();
					
					OMQLogger.getInstance().info("got OMQFile object from queue");
					OMQLogger.getInstance().info("file name is "+fileMessage.getOmqFileName());
					currFilename = fileMessage.getOmqFileName();
								
					currFilename = filenamePrefix+"__FileName-"+currFilename;
					OMQLogger.getInstance().info("new file name will be "+currFilename);
					
					File file = new File(ftPath+currFilename);
					FileOutputStream fileOutputStream = new FileOutputStream(file);	
				
					OMQLogger.getInstance().info("about to write "+ftPath+currFilename);
					
					fileOutputStream.write(fileMessage.getOmqFileContent());
					fileOutputStream.flush();
					fileOutputStream.close();

				}
				catch (Exception e)
				{
					OMQLogger.getInstance().error("it look like message origin is not from OMQ file system",e);
//					OmqLogger.getInstance().error("message body encoding is "+message.getBody_encoding());
					
					msgType = message.getMsgType().getBodyType();
//					msgEncoding = message.getMsgEncoding().getBodyEncoding();

//					if (msgType.compareToIgnoreCase("XML") == 0)
//					{
//						currFilename = filenamePrefix+unknownFilename+".xml";
//					}
//					else if (msgType.compareToIgnoreCase("CSV") == 0)
//					{
//						currFilename = filenamePrefix+unknownFilename+".csv";
//					}
//					else
//					{
//						currFilename = filenamePrefix+unknownFilename+".txt";
//					}

//					if (msgType.compareToIgnoreCase("other") == 0)
//					{
//						currFilename = filenamePrefix+unknownFilename+"."+msgEncoding.toLowerCase()+".txt"; //+msgType.toLowerCase();
//					}
//					else
//					{
//						currFilename = filenamePrefix+"__"+unknownFilename+"."+msgEncoding.toLowerCase()+"."+msgType.toLowerCase();
//					}
					currFilename = filenamePrefix+"__"+unknownFilename+"."+msgType.toLowerCase();
					
					OMQLogger.getInstance().error("message body type is "+msgType);
					
//					OmqLogger.getInstance().error("saving it to unknown file name");

					File file = new File(ftPath+currFilename);
					FileOutputStream fileOutputStream = new FileOutputStream(file);	
					
					OMQLogger.getInstance().info("about to write "+ftPath+currFilename);
					
					//fileOutputStream.write(OMQConversions.Base642ByteArray(message.getMessage_content()));
					fileOutputStream.write((message.getMsgBodyasByteArray()));
					
					fileOutputStream.flush();
					fileOutputStream.close();
				}
								
				OMQLogger.getInstance().info("writing file '"+ftPath+currFilename+"' finished succesfully");
				return true;
			}
		}
	
		catch (RemoteException e)
		{
			// TODO Auto-generated catch block
			OMQLogger.getInstance().error("error communicating with OMQ server",e);
			throw new OMQFileTransferException("error communicating with OMQ server");
		}		

		catch (EOFException e)
		{
			OMQLogger.getInstance().error("failed to write file, file content is corrupted",e);
			throw new OMQFileTransferException("failed to write file, file content is corrupted");
			
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			OMQLogger.getInstance().error("failed to write file",e);
			throw new OMQFileTransferException("failed to write file");
		}
//		catch (ClassNotFoundException e)
//		{
//			// TODO Auto-generated catch block
//			OmqLogger.getInstance().error("class cast exception, probably not a OMQFile message, cannot write to file",e);
//			throw new OMQFileTransferException("class cast exception, probably not a OMQFile message, cannot write to file");
//		}		
		catch (OMQPropertiesReaderException e) 
		{
			// TODO Auto-generated catch block
			OMQLogger.getInstance().error("failed to read property from properties file",e);
			throw new OMQFileTransferInitException("failed to read property from properties file");
		}		

	}
	
	
	private boolean lastActionSuccesfull()
	{
		OMQLogger.getInstance().info("message.getResponse_code() = "+session.getLastErrorCode());
		OMQLogger.getInstance().info("message.getResponse_msg_id() = "+session.getLastMessageID());
		OMQLogger.getInstance().info("message.getResponse_err_body() = "+session.getLastErrorDescription());
		if (new Integer(session.getLastErrorCode()).intValue() == 0)
		{
			return true;
		}
		else
		{
			return false;
		}
		
	}

	public void pause(int seconds) throws InterruptedException
	{
		wait(1000*seconds);
	}

	public String getFtPath() 
	{
		return ftPath;
	}    
	public void setFtPath(String path) 
	{
		ftPath=FileUtil.addSlashInFilename(path);
	}

	public String getLastMsgID() {
		return lastMsgID;
	}

	private void setLastMsgID(String lastMsgID) {
		this.lastMsgID = lastMsgID;
	}    
	
	public static String getVersion()
	{
		return ftVersion;
	}

	public String getOmqQueueManager() {
		return omqQueueManager;
	}

	public String getOmqQueue() {
		return omqQueue;
	}
}

